#include <sdk.h>
#include <algorithm>
#include <wx/tokenzr.h>
#include "editarraystringdlg.h"
#include "projectloader_hooks.h"
#include "M_Code_StyledTextCtrl.h"
#include "compilercommandgenerator.h"
#include <M_Code_debugger_interfaces.h>
#include "editbreakpointdlg.h"
#include "databreakpointdlg.h"
#include "debuggerdriver.h"
#include "Debugger.h"
#include "debuggeroptionsdlg.h"
#include "debuggeroptionsprjdlg.h"
#include "editwatchdlg.h"
#define implement_debugger_toolbar

enum DebugCommandConst {
  CMD_CONTINUE,
  CMD_STEP,
  CMD_STEPIN,
  CMD_STEPOUT,
  CMD_STEP_INSTR,
  CMD_STEP_INTO_INSTR,
  CMD_STOP,
  CMD_BACKTRACE,
  CMD_DISASSEMBLE,
  CMD_REGISTERS,
  CMD_MEMORYDUMP,
  CMD_RUNNINGTHREADS
};

const wxString g_EscapeChar = wxChar( 26 );

namespace {
  long idMenuInfoFrame = wxNewId();
  long idMenuInfoDLL = wxNewId();
  long idMenuInfoFiles = wxNewId();
  long idMenuInfoFPU = wxNewId();
  long idMenuInfoSignals = wxNewId();
  long idMenuInfoPrintElementsUnlimited = wxNewId();
  long idMenuInfoPrintElements20 = wxNewId();
  long idMenuInfoPrintElements50 = wxNewId();
  long idMenuInfoPrintElements100 = wxNewId();
  long idMenuInfoPrintElements200 = wxNewId();
  long idMenuInfoCatchThrow = wxNewId();
  long idGDBProcess = wxNewId();
  long idTimerPollDebugger = wxNewId();
  long idMenuWatchDereference = wxNewId();
  PluginRegistrant<DebuggerGDB> reg( _T( "Debugger" ) );
}

BEGIN_EVENT_TABLE( DebuggerGDB, cbDebuggerPlugin )
  EVT_MENU( idMenuInfoFrame, DebuggerGDB::OnInfoFrame )
  EVT_MENU( idMenuInfoDLL, DebuggerGDB::OnInfoDLL )
  EVT_MENU( idMenuInfoFiles, DebuggerGDB::OnInfoFiles )
  EVT_MENU( idMenuInfoFPU, DebuggerGDB::OnInfoFPU )
  EVT_MENU( idMenuInfoSignals, DebuggerGDB::OnInfoSignals )
  EVT_MENU( idMenuWatchDereference, DebuggerGDB::OnMenuWatchDereference )
  EVT_PIPEDPROCESS_STDOUT( idGDBProcess, DebuggerGDB::OnGDBOutput )
  EVT_PIPEDPROCESS_STDERR( idGDBProcess, DebuggerGDB::OnGDBError )
  EVT_PIPEDPROCESS_TERMINATED( idGDBProcess, DebuggerGDB::OnGDBTerminated )
  EVT_IDLE( DebuggerGDB::OnIdle )
  EVT_TIMER( idTimerPollDebugger, DebuggerGDB::OnTimer )
  EVT_COMMAND( -1, DEBUGGER_CURSOR_CHANGED, DebuggerGDB::OnCursorChanged )
  EVT_COMMAND( -1, DEBUGGER_SHOW_FILE_LINE, DebuggerGDB::OnShowFile )
  EVT_UPDATE_UI( idMenuInfoPrintElementsUnlimited, DebuggerGDB::OnUpdateTools )
  EVT_UPDATE_UI( idMenuInfoPrintElements20, DebuggerGDB::OnUpdateTools )
  EVT_UPDATE_UI( idMenuInfoPrintElements50, DebuggerGDB::OnUpdateTools )
  EVT_UPDATE_UI( idMenuInfoPrintElements100, DebuggerGDB::OnUpdateTools )
  EVT_UPDATE_UI( idMenuInfoPrintElements200, DebuggerGDB::OnUpdateTools )
  EVT_MENU( idMenuInfoPrintElementsUnlimited, DebuggerGDB::OnPrintElements )
  EVT_MENU( idMenuInfoPrintElements20, DebuggerGDB::OnPrintElements )
  EVT_MENU( idMenuInfoPrintElements50, DebuggerGDB::OnPrintElements )
  EVT_MENU( idMenuInfoPrintElements100, DebuggerGDB::OnPrintElements )
  EVT_MENU( idMenuInfoPrintElements200, DebuggerGDB::OnPrintElements )
  EVT_UPDATE_UI( idMenuInfoCatchThrow, DebuggerGDB::OnUpdateCatchThrow )
  EVT_MENU( idMenuInfoCatchThrow, DebuggerGDB::OnCatchThrow )
END_EVENT_TABLE()

DebuggerGDB::DebuggerGDB() :
  cbDebuggerPlugin( _T( "GDB/CDB debugger" ), _T( "gdb_debugger" ) ),
  m_State( this ),
  m_pProcess( 0L ),
  m_LastExitCode( 0 ),
  m_Pid( 0 ),
  m_PidToAttach( 0 ),
  m_NoDebugInfo( false ),
  m_StoppedOnSignal( false ),
  m_pProject( 0 ),
  m_bIsConsole( false ),
  m_stopDebuggerConsoleClosed( false ),
  m_nConsolePid( 0 ),
  m_TemporaryBreak( false ),
  m_printElements( 200 ) {
  if( !Manager::LoadResource( _T( "Debugger.zip" ) ) ) {
    NotifyMissingFile( _T( "Debugger.zip" ) );
  }
}

DebuggerGDB::~DebuggerGDB() {
}

void DebuggerGDB::OnAttachReal() {
  m_TimerPollDebugger.SetOwner( this, idTimerPollDebugger );
  ProjectLoaderHooks::HookFunctorBase* myhook = new ProjectLoaderHooks::HookFunctor<DebuggerGDB>( this, &DebuggerGDB::OnProjectLoadingHook );
  m_HookId = ProjectLoaderHooks::RegisterHook( myhook );
  Manager::Get()->RegisterEventSink( cbEVT_BUILDTARGET_SELECTED, new cbEventFunctor<DebuggerGDB, CodeBlocksEvent>( this, &DebuggerGDB::OnBuildTargetSelected ) );
}

void DebuggerGDB::OnReleaseReal( M_Code_unused bool appShutDown ) {
  ProjectLoaderHooks::UnregisterHook( m_HookId, true );
  if( m_State.HasDriver() ) {
    Stop();
    wxYieldIfNeeded();
  }
  m_State.CleanUp();
  KillConsole();
}

bool DebuggerGDB::SupportsFeature( cbDebuggerFeature::Flags flag ) {
  DebuggerConfiguration &config = GetActiveConfigEx();
  if( config.IsGDB() ) {
    switch( flag ) {
      case cbDebuggerFeature::Breakpoints:
      case cbDebuggerFeature::Callstack:
      case cbDebuggerFeature::CPURegisters:
      case cbDebuggerFeature::Disassembly:
      case cbDebuggerFeature::Watches:
      case cbDebuggerFeature::ValueTooltips:
      case cbDebuggerFeature::ExamineMemory:
      case cbDebuggerFeature::Threads:
      case cbDebuggerFeature::RunToCursor:
      case cbDebuggerFeature::SetNextStatement:
        return true;
      default:
        return false;
    }
  } else {
    switch( flag ) {
      case cbDebuggerFeature::Breakpoints:
      case cbDebuggerFeature::Callstack:
      case cbDebuggerFeature::CPURegisters:
      case cbDebuggerFeature::Disassembly:
      case cbDebuggerFeature::Watches:
      case cbDebuggerFeature::ValueTooltips:
        return true;
      case cbDebuggerFeature::ExamineMemory:
      case cbDebuggerFeature::Threads:
      case cbDebuggerFeature::RunToCursor:
      case cbDebuggerFeature::SetNextStatement:
      default:
        return false;
    }
  }
  return false;
}

cbDebuggerConfiguration* DebuggerGDB::LoadConfig( const ConfigManagerWrapper &config ) {
  return new DebuggerConfiguration( config );
}

DebuggerConfiguration& DebuggerGDB::GetActiveConfigEx() {
  return static_cast<DebuggerConfiguration&>( GetActiveConfig() );
}

cbConfigurationPanel* DebuggerGDB::GetProjectConfigurationPanel( wxWindow* parent, M_Code_Project* project ) {
  DebuggerOptionsProjectDlg* dlg = new DebuggerOptionsProjectDlg( parent, this, project );
  return dlg;
}

void DebuggerGDB::OnConfigurationChange( M_Code_unused bool isActive ) {
  DebuggerConfiguration &config = GetActiveConfigEx();
  bool locals = config.GetFlag( DebuggerConfiguration::WatchLocals );
  bool funcArgs = config.GetFlag( DebuggerConfiguration::WatchFuncArgs );
  cbWatchesDlg *watchesDialog = Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
  bool update = false;
  if( !locals ) {
    if( m_localsWatch ) {
      watchesDialog->RemoveWatch( m_localsWatch );
      m_localsWatch = cb::shared_ptr<GDBWatch>();
    }
  } else if( !m_localsWatch ) {
    update = true;
  }
  if( !funcArgs ) {
    if( m_funcArgsWatch ) {
      watchesDialog->RemoveWatch( m_funcArgsWatch );
      m_funcArgsWatch = cb::shared_ptr<GDBWatch>();
    }
  } else if( !m_funcArgsWatch ) {
    update = true;
  }
  if( update ) {
    RequestUpdate( cbDebuggerPlugin::Watches );
  }
}

wxArrayString& DebuggerGDB::GetSearchDirs( M_Code_Project* prj ) {
  SearchDirsMap::iterator it = m_SearchDirs.find( prj );
  if( it == m_SearchDirs.end() ) {
    it = m_SearchDirs.insert( m_SearchDirs.begin(), std::make_pair( prj, wxArrayString() ) );
  }
  return it->second;
}

RemoteDebuggingMap& DebuggerGDB::GetRemoteDebuggingMap( M_Code_Project* project ) {
  if( !project ) {
    project = m_pProject;
  }
  ProjectRemoteDebuggingMap::iterator it = m_RemoteDebugging.find( project );
  if( it == m_RemoteDebugging.end() ) {
    it = m_RemoteDebugging.insert( m_RemoteDebugging.begin(), std::make_pair( project, RemoteDebuggingMap() ) );
  }
  return it->second;
}


void DebuggerGDB::OnProjectLoadingHook( M_Code_Project* project, TiXmlElement* elem, bool loading ) {
  wxArrayString& pdirs = GetSearchDirs( project );
  RemoteDebuggingMap& rdprj = GetRemoteDebuggingMap( project );
  if( loading ) {
    rdprj.clear();
    TiXmlElement* conf = elem->FirstChildElement( "debugger" );
    if( conf ) {
      TiXmlElement* pathsElem = conf->FirstChildElement( "search_path" );
      while( pathsElem ) {
        if( pathsElem->Attribute( "add" ) ) {
          wxString dir = cbC2U( pathsElem->Attribute( "add" ) );
          if( pdirs.Index( dir ) == wxNOT_FOUND ) {
            pdirs.Add( dir );
          }
        }
        pathsElem = pathsElem->NextSiblingElement( "search_path" );
      }
      TiXmlElement* rdElem = conf->FirstChildElement( "remote_debugging" );
      while( rdElem ) {
        wxString targetName = cbC2U( rdElem->Attribute( "target" ) );
        ProjectBuildTarget* bt = project->GetBuildTarget( targetName );
        TiXmlElement* rdOpt = rdElem->FirstChildElement( "options" );
        if( rdOpt ) {
          RemoteDebugging rd;
          if( rdOpt->Attribute( "conn_type" ) ) {
            rd.connType = ( RemoteDebugging::ConnectionType )atol( rdOpt->Attribute( "conn_type" ) );
          }
          if( rdOpt->Attribute( "serial_port" ) ) {
            rd.serialPort = cbC2U( rdOpt->Attribute( "serial_port" ) );
          }
          if( rdOpt->Attribute( "serial_baud" ) ) {
            rd.serialBaud = cbC2U( rdOpt->Attribute( "serial_baud" ) );
          }
          if( rdOpt->Attribute( "ip_address" ) ) {
            rd.ip = cbC2U( rdOpt->Attribute( "ip_address" ) );
          }
          if( rdOpt->Attribute( "ip_port" ) ) {
            rd.ipPort = cbC2U( rdOpt->Attribute( "ip_port" ) );
          }
          if( rdOpt->Attribute( "additional_cmds" ) ) {
            rd.additionalCmds = cbC2U( rdOpt->Attribute( "additional_cmds" ) );
          }
          if( rdOpt->Attribute( "additional_cmds_before" ) ) {
            rd.additionalCmdsBefore = cbC2U( rdOpt->Attribute( "additional_cmds_before" ) );
          }
          if( rdOpt->Attribute( "skip_ld_path" ) ) {
            rd.skipLDpath = cbC2U( rdOpt->Attribute( "skip_ld_path" ) ) != _T( "0" );
          }
          if( rdOpt->Attribute( "extended_remote" ) ) {
            rd.extendedRemote = cbC2U( rdOpt->Attribute( "extended_remote" ) ) != _T( "0" );
          }
          if( rdOpt->Attribute( "additional_shell_cmds_after" ) ) {
            rd.additionalShellCmdsAfter = cbC2U( rdOpt->Attribute( "additional_shell_cmds_after" ) );
          }
          if( rdOpt->Attribute( "additional_shell_cmds_before" ) ) {
            rd.additionalShellCmdsBefore = cbC2U( rdOpt->Attribute( "additional_shell_cmds_before" ) );
          }
          rdprj.insert( rdprj.end(), std::make_pair( bt, rd ) );
        }
        rdElem = rdElem->NextSiblingElement( "remote_debugging" );
      }
    }
  } else {
    TiXmlElement* node = elem->FirstChildElement( "debugger" );
    if( !node ) {
      node = elem->InsertEndChild( TiXmlElement( "debugger" ) )->ToElement();
    }
    node->Clear();
    if( pdirs.GetCount() > 0 ) {
      for( size_t i = 0; i < pdirs.GetCount(); ++i ) {
        TiXmlElement* path = node->InsertEndChild( TiXmlElement( "search_path" ) )->ToElement();
        path->SetAttribute( "add", cbU2C( pdirs[i] ) );
      }
    }
    if( rdprj.size() ) {
      for( RemoteDebuggingMap::iterator it = rdprj.begin(); it != rdprj.end(); ++it ) {
        RemoteDebugging& rd = it->second;
        if( rd.serialPort.IsEmpty() && rd.ip.IsEmpty() &&
            rd.additionalCmds.IsEmpty() && rd.additionalCmdsBefore.IsEmpty() &&
            !rd.skipLDpath && !rd.extendedRemote ) {
          continue;
        }
        TiXmlElement* rdnode = node->InsertEndChild( TiXmlElement( "remote_debugging" ) )->ToElement();
        if( it->first ) {
          rdnode->SetAttribute( "target", cbU2C( it->first->GetTitle() ) );
        }
        TiXmlElement* tgtnode = rdnode->InsertEndChild( TiXmlElement( "options" ) )->ToElement();
        tgtnode->SetAttribute( "conn_type", ( int )rd.connType );
        if( !rd.serialPort.IsEmpty() ) {
          tgtnode->SetAttribute( "serial_port", cbU2C( rd.serialPort ) );
        }
        if( !rd.serialBaud.IsEmpty() ) {
          tgtnode->SetAttribute( "serial_baud", cbU2C( rd.serialBaud ) );
        }
        if( !rd.ip.IsEmpty() ) {
          tgtnode->SetAttribute( "ip_address", cbU2C( rd.ip ) );
        }
        if( !rd.ipPort.IsEmpty() ) {
          tgtnode->SetAttribute( "ip_port", cbU2C( rd.ipPort ) );
        }
        if( !rd.additionalCmds.IsEmpty() ) {
          tgtnode->SetAttribute( "additional_cmds", cbU2C( rd.additionalCmds ) );
        }
        if( !rd.additionalCmdsBefore.IsEmpty() ) {
          tgtnode->SetAttribute( "additional_cmds_before", cbU2C( rd.additionalCmdsBefore ) );
        }
        if( rd.skipLDpath ) {
          tgtnode->SetAttribute( "skip_ld_path", "1" );
        }
        if( rd.extendedRemote ) {
          tgtnode->SetAttribute( "extended_remote", "1" );
        }
        if( !rd.additionalShellCmdsAfter.IsEmpty() ) {
          tgtnode->SetAttribute( "additional_shell_cmds_after", cbU2C( rd.additionalShellCmdsAfter ) );
        }
        if( !rd.additionalShellCmdsBefore.IsEmpty() ) {
          tgtnode->SetAttribute( "additional_shell_cmds_before", cbU2C( rd.additionalShellCmdsBefore ) );
        }
      }
    }
  }
}

void DebuggerGDB::DoWatches() {
  if( !m_pProcess ) {
    return;
  }
  DebuggerConfiguration &config = GetActiveConfigEx();
  bool locals = config.GetFlag( DebuggerConfiguration::WatchLocals );
  bool funcArgs = config.GetFlag( DebuggerConfiguration::WatchFuncArgs );
  if( locals ) {
    if( m_localsWatch == nullptr ) {
      m_localsWatch = cb::shared_ptr<GDBWatch>( new GDBWatch( _T( "Locals" ) ) );
      m_localsWatch->Expand( true );
      m_localsWatch->MarkAsChanged( false );
      cbWatchesDlg *watchesDialog = Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
      watchesDialog->AddSpecialWatch( m_localsWatch, true );
    }
  }
  if( funcArgs ) {
    if( m_funcArgsWatch == nullptr ) {
      m_funcArgsWatch = cb::shared_ptr<GDBWatch>( new GDBWatch( _T( "Function arguments" ) ) );
      m_funcArgsWatch->Expand( true );
      m_funcArgsWatch->MarkAsChanged( false );
      cbWatchesDlg *watchesDialog = Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
      watchesDialog->AddSpecialWatch( m_funcArgsWatch, true );
    }
  }
  m_State.GetDriver()->UpdateWatches( m_localsWatch, m_funcArgsWatch, m_watches );
}

int DebuggerGDB::LaunchProcess( const wxString& cmd, const wxString& cwd ) {
  if( m_pProcess ) {
    return -1;
  }
  m_pProcess = new PipedProcess( &m_pProcess, this, idGDBProcess, true, cwd );
  Log( _T( "启动调试器: " ) + cmd );
  m_Pid = wxExecute( cmd, wxEXEC_ASYNC, m_pProcess );
  if( !m_Pid ) {
    delete m_pProcess;
    m_pProcess = 0;
    Log( _T( "失败" ), Logger::error );
    return -1;
  } else if( !m_pProcess->GetOutputStream() ) {
    delete m_pProcess;
    m_pProcess = 0;
    Log( _T( "失败 (调试器 stdin 丢失)" ), Logger::error );
    return -2;
  } else if( !m_pProcess->GetInputStream() ) {
    delete m_pProcess;
    m_pProcess = 0;
    Log( _T( "失败 (调试器 stdout 丢失)" ), Logger::error );
    return -2;
  } else if( !m_pProcess->GetErrorStream() ) {
    delete m_pProcess;
    m_pProcess = 0;
    Log( _T( "失败 (调试器 stderr 丢失)" ), Logger::error );
    return -2;
  }
  Log( _T( "done" ) );
  return 0;
}

bool DebuggerGDB::IsStopped() const {
  return !m_State.HasDriver() || m_State.GetDriver()->IsProgramStopped();
}

bool DebuggerGDB::IsBusy() const {
  return m_State.HasDriver() && m_State.GetDriver()->IsQueueBusy();
}

bool DebuggerGDB::Debug( bool breakOnEntry ) {
  if( m_pProcess || WaitingCompilerToFinish() ) {
    return false;
  }
  m_pProject = 0;
  m_NoDebugInfo = false;
  ProjectManager* prjMan = Manager::Get()->GetProjectManager();
  M_Code_Project* project = prjMan->GetActiveProject();
  if( !project && m_PidToAttach == 0 ) {
    return false;
  }
  m_pProject = project;
  if( m_pProject && m_ActiveBuildTarget.IsEmpty() ) {
    m_ActiveBuildTarget = m_pProject->GetActiveBuildTarget();
  }
  m_Canceled = false;
  if( !EnsureBuildUpToDate( breakOnEntry ? StartTypeStepInto : StartTypeRun ) ) {
    return false;
  }
  if( !WaitingCompilerToFinish() && !m_State.HasDriver() && !m_Canceled ) {
    return DoDebug( breakOnEntry ) == 0;
  }
  return true;
}

int DebuggerGDB::DoDebug( bool breakOnEntry ) {
  m_Canceled = false;
  m_bIsConsole = false;
  m_nConsolePid = 0;
  ProjectManager* prjMan = Manager::Get()->GetProjectManager();
  ProjectBuildTarget* target = 0;
  Compiler* actualCompiler = 0;
  if( ( m_PidToAttach == 0 ) && m_pProject ) {
    Log( _T( "选择目标: " ) );
    if( !m_pProject->BuildTargetValid( m_ActiveBuildTarget, false ) ) {
      int tgtIdx = m_pProject->SelectTarget();
      if( tgtIdx == -1 ) {
        Log( _T( "已取消" ) );
        m_Canceled = true;
        return 3;
      }
      target = m_pProject->GetBuildTarget( tgtIdx );
      m_ActiveBuildTarget = ( target ? target->GetTitle() : wxString( wxEmptyString ) );
    } else
    { target = m_pProject->GetBuildTarget( m_ActiveBuildTarget ); }
    if( target && target->GetTargetType() == ttCommandsOnly ) {
      InfoWindow::Tip( _T( "所选目标仅运行 生成前/生成后 步骤命令无法调试此类目标" ), _T( "错误" ), 3500 );
      return 3;
    }
    if( target ) {
      Log( target->GetTitle() );
    }
    actualCompiler = CompilerFactory::GetCompiler( target ? target->GetCompilerID()
                     : m_pProject->GetCompilerID() );
  } else {
    actualCompiler = CompilerFactory::GetDefaultCompiler();
  }
  if( !actualCompiler ) {
    wxString msg;
    msg.Printf( _T( "这个 %s 配置为无效的调试器.\n操作失败..." ), target ? _T( "target" ) : _T( "project" ) );
    InfoWindow::Tip( msg, _T( "错误" ), 3500 );
    m_Canceled = true;
    return 9;
  }
  wxString cmdexe;
  cmdexe = GetActiveConfigEx().GetDebuggerExecutable();
  cmdexe.Trim();
  cmdexe.Trim( true );
  if( cmdexe.IsEmpty() ) {
    InfoWindow::Tip( _T( "需要设置调试器参数" ), _T( "错误" ), 3500 );
    m_Canceled = true;
    return -1;
  }
  if( !m_State.StartDriver( target ) ) {
    InfoWindow::Tip( _T( "无法决定要使用哪个调试器" ), _T( "错误" ), 3500 );
    m_Canceled = true;
    return -1;
  }
  PluginManager *plm = Manager::Get()->GetPluginManager();
  CodeBlocksEvent evt( cbEVT_DEBUGGER_STARTED );
  plm->NotifyPlugins( evt );
  int nRet = evt.GetInt();
  if( nRet < 0 ) {
    InfoWindow::Tip( _T( "插件中断了调试过程" ), _T( "提示" ), 2500 );
    m_Canceled = true;
    return -1;
  }
  wxString cmd;
  wxString cmdline;
  if( m_PidToAttach == 0 ) {
    m_State.GetDriver()->ClearDirectories();
    if( GetActiveConfigEx().GetFlag( DebuggerConfiguration::AddOtherProjectDirs ) ) {
      ProjectsArray* projects = prjMan->GetProjects();
      for( unsigned int i = 0; i < projects->GetCount(); ++i ) {
        M_Code_Project* it = projects->Item( i );
        if( it == m_pProject ) {
          continue;
        }
        AddSourceDir( it->GetBasePath() );
        AddSourceDir( it->GetCommonTopLevelPath() );
      }
    }
    wxArrayString& pdirs = GetSearchDirs( m_pProject );
    for( size_t i = 0; i < pdirs.GetCount(); ++i ) {
      AddSourceDir( pdirs[i] );
    }
    if( m_pProject ) {
      AddSourceDir( m_pProject->GetBasePath() );
      AddSourceDir( m_pProject->GetCommonTopLevelPath() );
    }
    wxString debuggee, path;
    if( !GetDebuggee( debuggee, path, target ) ) {
      m_Canceled = true;
      return -3;
    }
    if( !path.empty() ) {
      ConvertToGDBDirectory( path );
      if( path != _T( "." ) ) {
        Log( _T( "正在将目录更改为: " ) + path );
        m_State.GetDriver()->SetWorkingDirectory( path );
      }
    }
    if( target && !target->GetExecutionParameters().IsEmpty() ) {
      m_State.GetDriver()->SetArguments( target->GetExecutionParameters() );
    }
    cmdline = m_State.GetDriver()->GetCommandLine( cmdexe, debuggee, GetActiveConfigEx().GetUserArguments() );
  } else {
    cmdline = m_State.GetDriver()->GetCommandLine( cmdexe, m_PidToAttach, GetActiveConfigEx().GetUserArguments() );
  }
  RemoteDebuggingMap& rdprj = GetRemoteDebuggingMap();
  RemoteDebugging rd = rdprj[0];
  RemoteDebuggingMap::iterator it = rdprj.find( target );
  if( it != rdprj.end() ) {
    rd.MergeWith( it->second );
  }
  wxString oldLibPath;
  if( !rd.skipLDpath ) {
    wxGetEnv( CB_LIBRARY_ENVVAR, &oldLibPath );
    if( actualCompiler && target ) {
      wxString newLibPath;
      const wxString libPathSep =  _T( ":" );
      newLibPath << _T( "." ) << libPathSep;
      CompilerCommandGenerator *generator = actualCompiler->GetCommandGenerator( m_pProject );
      newLibPath << GetStringFromArray( generator->GetLinkerSearchDirs( target ), libPathSep );
      delete generator;
      if( newLibPath.Mid( newLibPath.Length() - 1, 1 ) != libPathSep ) {
        newLibPath << libPathSep;
      }
      newLibPath << oldLibPath;
      wxSetEnv( CB_LIBRARY_ENVVAR, newLibPath );
      Log( wxString( _T( "集合变量: " ) ) + CB_LIBRARY_ENVVAR _T( "=" ) + newLibPath );
    }
  }
  wxString wdir = m_State.GetDriver()->GetDebuggersWorkingDirectory();
  if( wdir.empty() ) {
    wdir = m_pProject ? m_pProject->GetBasePath() : _T( "." );
  }
  DebugLog( _T( "命令行: " ) + cmdline );
  DebugLog( _T( "工作目录: " ) + wdir );
  int ret = LaunchProcess( cmdline, wdir );
  if( !rd.skipLDpath ) {
    wxSetEnv( CB_LIBRARY_ENVVAR, oldLibPath );
  }
  if( ret != 0 ) {
    m_Canceled = true;
    return ret;
  }
  wxString out;
  m_TimerPollDebugger.Start( 20 );
  int i = 50;
  while( i ) {
    wxMilliSleep( 1 );
    Manager::Yield();
    --i;
  }
  if( !m_State.HasDriver() ) {
    return -1;
  }
  bool isConsole = ( target && target->GetTargetType() == ttConsoleOnly );
  m_State.GetDriver()->Prepare( isConsole, m_printElements );
  m_State.ApplyBreakpoints();
  m_bIsConsole = target && target->GetUseConsoleRunner();
  if( m_bIsConsole ) {
    wxString consoleTty;
    m_nConsolePid = RunNixConsole( consoleTty );
    if( m_nConsolePid > 0 ) {
      m_stopDebuggerConsoleClosed = true;
      wxString gdbTtyCmd;
      gdbTtyCmd << _T( "tty " ) << consoleTty;
      m_State.GetDriver()->QueueCommand( new DebuggerCmd( m_State.GetDriver(), gdbTtyCmd, true ) );
      DebugLog( F( _T( "Queued:[%s]" ), gdbTtyCmd.wx_str() ) );
    }
  }
  if( m_PidToAttach == 0 ) {
    m_State.GetDriver()->Start( breakOnEntry );
  } else {
    m_State.GetDriver()->Attach( m_PidToAttach );
  }
  if( m_pProcess ) {
    SwitchToDebuggingLayout();
  }
  return 0;
}

void DebuggerGDB::AddSourceDir( const wxString& dir ) {
  if( !m_State.HasDriver() || dir.IsEmpty() ) {
    return;
  }
  wxString filename = dir;
  Manager::Get()->GetMacrosManager()->ReplaceEnvVars( filename );
  Log( _T( "添加源目录: " ) + filename );
  ConvertToGDBDirectory( filename, _T( "" ), false );
  m_State.GetDriver()->AddDirectory( filename );
}

void DebuggerGDB::StripQuotes( wxString& str ) {
  if( str.GetChar( 0 ) == _T( '\"' ) && str.GetChar( str.Length() - 1 ) == _T( '\"' ) ) {
    str = str.Mid( 1, str.Length() - 2 );
  }
}

void DebuggerGDB::ConvertToGDBFriendly( wxString& str ) {
  if( str.IsEmpty() ) {
    return;
  }
  str = UnixFilename( str );
  while( str.Replace( _T( "\\" ), _T( "/" ) ) ) ;
  while( str.Replace( _T( "//" ), _T( "/" ) ) ) ;
  if( str.Find( _T( ' ' ) ) != -1 && str.GetChar( 0 ) != _T( '"' ) ) {
    str = _T( "\"" ) + str + _T( "\"" );
  }
}

void DebuggerGDB::ConvertToGDBFile( wxString& str ) {
  wxFileName fname = str;
  str = fname.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
  DebuggerGDB::ConvertToGDBDirectory( str );
  str << fname.GetFullName();
}

void DebuggerGDB::ConvertDirectory( wxString& str, wxString base, bool relative ) {
  ConvertToGDBDirectory( str, base, relative );
}


void DebuggerGDB::ConvertToGDBDirectory( wxString& str, wxString base, bool relative ) {
  if( str.IsEmpty() ) {
    return;
  }
  ConvertToGDBFriendly( str );
  ConvertToGDBFriendly( base );
  StripQuotes( str );
  StripQuotes( base );
  if( ( str.GetChar( 0 ) != _T( '/' ) && str.GetChar( 0 ) != _T( '~' ) ) || base.IsEmpty() ) {
    relative = false;
  }
  if( relative ) {
    if( str.GetChar( 0 ) == _T( '/' ) ) {
      str  = str.Mid( 1, str.Length() );
    } else if( str.GetChar( 0 ) == _T( '~' ) ) {
      str  = str.Mid( 2, str.Length() );
    }
    if( base.GetChar( 0 ) == _T( '/' ) ) {
      base = base.Mid( 1, base.Length() );
    } else if( base.GetChar( 0 ) == _T( '~' ) ) {
      base = base.Mid( 2, base.Length() );
    }
    while( !base.IsEmpty() && !str.IsEmpty() ) {
      if( str.BeforeFirst( _T( '/' ) ) == base.BeforeFirst( _T( '/' ) ) ) {
        if( str.Find( _T( '/' ) ) == wxNOT_FOUND ) {
          str.Clear();
        } else
        { str = str.AfterFirst( _T( '/' ) ); }
        if( base.Find( _T( '/' ) ) == wxNOT_FOUND ) {
          base.Clear();
        } else
        { base = base.AfterFirst( _T( '/' ) ); }
      } else
      { break; }
    }
    while( !base.IsEmpty() ) {
      str = _T( "../" ) + str;
      if( base.Find( _T( '/' ) ) == wxNOT_FOUND ) {
        base.Clear();
      } else
      { base = base.AfterFirst( _T( '/' ) ); }
    }
  }
  ConvertToGDBFriendly( str );
}

void DebuggerGDB::SendCommand( const wxString& cmd, bool debugLog ) {
  const wxString &cleandCmd = CleanStringValue( cmd );
  if( !debugLog ) {
    Log( _T( "> " ) + cleandCmd );
  }
  if( debugLog ) {
    DoSendCommand( cleandCmd );
  } else if( m_State.HasDriver() ) {
    m_State.GetDriver()->QueueCommand( new DebuggerCmd( m_State.GetDriver(), cleandCmd, true ) );
  }
}

void DebuggerGDB::DoSendCommand( const wxString& cmd ) {
  if( !m_pProcess || !IsStopped() ) {
    return;
  }
  if( HasDebugLog() ) {
    DebugLog( _T( "> " ) + cmd );
  }
  m_pProcess->SendString( cmd );
}

void DebuggerGDB::RequestUpdate( DebugWindows window ) {
  switch( window ) {
    case Backtrace:
      RunCommand( CMD_BACKTRACE );
      break;
    case CPURegisters:
      RunCommand( CMD_REGISTERS );
      break;
    case Disassembly:
      RunCommand( CMD_DISASSEMBLE );
      break;
    case ExamineMemory:
      RunCommand( CMD_MEMORYDUMP );
      break;
    case Threads:
      RunCommand( CMD_RUNNINGTHREADS );
      break;
    case Watches:
      if( IsWindowReallyShown( Manager::Get()->GetDebuggerManager()->GetWatchesDialog()->GetWindow() ) ) {
        DoWatches();
      }
      break;
    default:
      break;
  }
}

void DebuggerGDB::RunCommand( int cmd ) {
  if( !m_pProcess ) {
    return;
  }
  switch( cmd ) {
    case CMD_CONTINUE: {
      ClearActiveMarkFromAllEditors();
      if( m_State.HasDriver() ) {
        Log( _T( "Continuing..." ) );
        m_State.GetDriver()->Continue();
        m_State.GetDriver()->ResetCurrentFrame();
      }
      break;
    }
    case CMD_STEP: {
      ClearActiveMarkFromAllEditors();
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->Step();
        m_State.GetDriver()->ResetCurrentFrame();
      }
      break;
    }
    case CMD_STEP_INSTR: {
      ClearActiveMarkFromAllEditors();
      if( !Manager::Get()->GetDebuggerManager()->UpdateDisassembly() ) {
        RunCommand( CMD_DISASSEMBLE );
      }
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->StepInstruction();
        m_State.GetDriver()->ResetCurrentFrame();
        m_State.GetDriver()->NotifyCursorChanged();
      }
      break;
    }
    case CMD_STEP_INTO_INSTR: {
      ClearActiveMarkFromAllEditors();
      if( !Manager::Get()->GetDebuggerManager()->UpdateDisassembly() ) {
        RunCommand( CMD_DISASSEMBLE );
      }
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->StepIntoInstruction();
        m_State.GetDriver()->ResetCurrentFrame();
        m_State.GetDriver()->NotifyCursorChanged();
      }
      break;
    }
    case CMD_STEPIN: {
      ClearActiveMarkFromAllEditors();
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->StepIn();
        m_State.GetDriver()->ResetCurrentFrame();
      }
      break;
    }
    case CMD_STEPOUT: {
      ClearActiveMarkFromAllEditors();
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->StepOut();
        m_State.GetDriver()->ResetCurrentFrame();
      }
      break;
    }
    case CMD_STOP: {
      ClearActiveMarkFromAllEditors();
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->Stop();
        m_State.GetDriver()->ResetCurrentFrame();
        MarkAsStopped();
      }
      break;
    }
    case CMD_BACKTRACE: {
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->Backtrace();
      }
      break;
    }
    case CMD_DISASSEMBLE: {
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->Disassemble();
      }
      break;
    }
    case CMD_REGISTERS: {
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->CPURegisters();
      }
      break;
    }
    case CMD_MEMORYDUMP: {
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->MemoryDump();
      }
      break;
    }
    case CMD_RUNNINGTHREADS: {
      if( m_State.HasDriver() ) {
        m_State.GetDriver()->RunningThreads();
      }
      break;
    }
    default:
      break;
  }
}

int DebuggerGDB::GetStackFrameCount() const {
  return m_State.GetDriver()->GetStackFrames().size();
}

cb::shared_ptr<const cbStackFrame> DebuggerGDB::GetStackFrame( int index ) const {
  return m_State.GetDriver()->GetStackFrames()[index];
}

void DebuggerGDB::SwitchToFrame( int number ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->SetCurrentFrame( number, true );
    m_State.GetDriver()->SwitchToFrame( number );
    if( Manager::Get()->GetDebuggerManager()->UpdateBacktrace() ) {
      Manager::Get()->GetDebuggerManager()->GetBacktraceDialog()->Reload();
    }
  }
}

int DebuggerGDB::GetActiveStackFrame() const {
  return m_State.HasDriver() ? m_State.GetDriver()->GetCurrentFrame() : 0;
}

int DebuggerGDB::GetThreadsCount() const {
  if( !m_State.HasDriver() ) {
    return 0;
  } else {
    return m_State.GetDriver()->GetThreads().size();
  }
}

cb::shared_ptr<const cbThread> DebuggerGDB::GetThread( int index ) const {
  return m_State.GetDriver()->GetThreads()[index];
}

bool DebuggerGDB::SwitchToThread( int thread_number ) {
  if( !m_State.HasDriver() ) {
    return false;
  }
  DebuggerDriver *driver = m_State.GetDriver();
  DebuggerDriver::ThreadsContainer const &threads = driver->GetThreads();
  for( DebuggerDriver::ThreadsContainer::const_iterator it = threads.begin(); it != threads.end(); ++it ) {
    if( ( *it )->GetNumber() == thread_number ) {
      if( !( *it )->IsActive() ) {
        driver->SwitchThread( thread_number );
      }
      return true;
    }
  }
  return false;
}

cb::shared_ptr<cbBreakpoint> DebuggerGDB::AddBreakpoint( const wxString& filename, int line ) {
  bool debuggerIsRunning = !IsStopped();
  if( debuggerIsRunning ) {
    DoBreak( true );
  }
  cb::shared_ptr<DebuggerBreakpoint> bp = m_State.AddBreakpoint( filename, line, false );
  if( debuggerIsRunning ) {
    Continue();
  }
  return bp;
}

cb::shared_ptr<cbBreakpoint> DebuggerGDB::AddDataBreakpoint( const wxString& dataExpression ) {
  DataBreakpointDlg dlg( Manager::Get()->GetAppWindow(), dataExpression, true, 1 );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    const wxString& newDataExpression = dlg.GetDataExpression();
    int sel = dlg.GetSelection();
    cb::shared_ptr<DebuggerBreakpoint> bp = m_State.AddBreakpoint( newDataExpression, sel != 1, sel != 0 );
    return bp;
  } else {
    return cb::shared_ptr<cbBreakpoint>();
  }
}

int DebuggerGDB::GetBreakpointsCount() const {
  return m_State.GetBreakpoints().size();
}

cb::shared_ptr<cbBreakpoint> DebuggerGDB::GetBreakpoint( int index ) {
  BreakpointsList::const_iterator it = m_State.GetBreakpoints().begin();
  std::advance( it, index );
  cbAssert( it != m_State.GetBreakpoints().end() );
  return *it;
}

cb::shared_ptr<const cbBreakpoint> DebuggerGDB::GetBreakpoint( int index ) const {
  BreakpointsList::const_iterator it = m_State.GetBreakpoints().begin();
  std::advance( it, index );
  cbAssert( it != m_State.GetBreakpoints().end() );
  return *it;
}

void DebuggerGDB::UpdateBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint ) {
  const BreakpointsList &breakpoints = m_State.GetBreakpoints();
  BreakpointsList::const_iterator it = std::find( breakpoints.begin(), breakpoints.end(), breakpoint );
  if( it == breakpoints.end() ) {
    return;
  }
  cb::shared_ptr<DebuggerBreakpoint> bp = cb::static_pointer_cast<DebuggerBreakpoint>( breakpoint );
  bool reset = false;
  switch( bp->type ) {
    case DebuggerBreakpoint::bptCode: {
      EditBreakpointDlg dlg( *bp, Manager::Get()->GetAppWindow() );
      PlaceWindow( &dlg );
      if( dlg.ShowModal() == wxID_OK ) {
        *bp = dlg.GetBreakpoint();
        reset = true;
      }
      break;
    }
    case DebuggerBreakpoint::bptData: {
      int old_sel = 0;
      if( bp->breakOnRead && bp->breakOnWrite ) {
        old_sel = 2;
      } else if( !bp->breakOnRead && bp->breakOnWrite ) {
        old_sel = 1;
      }
      DataBreakpointDlg dlg( Manager::Get()->GetAppWindow(), bp->breakAddress, bp->enabled, old_sel );
      PlaceWindow( &dlg );
      if( dlg.ShowModal() == wxID_OK ) {
        bp->enabled = dlg.IsEnabled();
        bp->breakOnRead = dlg.GetSelection() != 1;
        bp->breakOnWrite = dlg.GetSelection() != 0;
        bp->breakAddress = dlg.GetDataExpression();
        reset = true;
      }
      break;
    }
    case DebuggerBreakpoint::bptFunction:
    default:
      return;
  }
  if( reset ) {
    bool debuggerIsRunning = !IsStopped();
    if( debuggerIsRunning ) {
      DoBreak( true );
    }
    m_State.ResetBreakpoint( bp );
    if( debuggerIsRunning ) {
      Continue();
    }
  }
}

void DebuggerGDB::DeleteBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint ) {
  bool debuggerIsRunning = !IsStopped();
  if( debuggerIsRunning ) {
    DoBreak( true );
  }
  m_State.RemoveBreakpoint( cb::static_pointer_cast<DebuggerBreakpoint>( breakpoint ) );
  if( debuggerIsRunning ) {
    Continue();
  }
}

void DebuggerGDB::DeleteAllBreakpoints() {
  bool debuggerIsRunning = !IsStopped();
  if( debuggerIsRunning ) {
    DoBreak( true );
  }
  m_State.RemoveAllBreakpoints();
  if( debuggerIsRunning ) {
    Continue();
  }
}

void DebuggerGDB::ShiftBreakpoint( int index, int lines_to_shift ) {
  BreakpointsList breakpoints = m_State.GetBreakpoints();
  BreakpointsList::iterator it = breakpoints.begin();
  std::advance( it, index );
  if( it != breakpoints.end() ) {
    m_State.ShiftBreakpoint( *it, lines_to_shift );
  }
}

void DebuggerGDB::EnableBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint, bool enable ) {
  bool debuggerIsRunning = !IsStopped();
  DebugLog( F( _T( "DebuggerGDB::EnableBreakpoint(running=%d);" ), debuggerIsRunning ? 1 : 0 ) );
  if( debuggerIsRunning ) {
    DoBreak( true );
  }
  cb::shared_ptr<DebuggerBreakpoint> bp = cb::static_pointer_cast<DebuggerBreakpoint>( breakpoint );
  bp->enabled = enable;
  m_State.ResetBreakpoint( bp );
  if( debuggerIsRunning ) {
    Continue();
  }
}

void DebuggerGDB::DeleteAllProjectBreakpoints( M_Code_Project* project ) {
  m_State.RemoveAllProjectBreakpoints( project );
}

void DebuggerGDB::Continue() {
  RunCommand( CMD_CONTINUE );
}

void DebuggerGDB::Next() {
  RunCommand( CMD_STEP );
}

void DebuggerGDB::NextInstruction() {
  RunCommand( CMD_STEP_INSTR );
}

void DebuggerGDB::StepIntoInstruction() {
  RunCommand( CMD_STEP_INTO_INSTR );
}

void DebuggerGDB::Step() {
  RunCommand( CMD_STEPIN );
}

bool DebuggerGDB::Validate( const wxString& line, const char cb ) {
  bool bResult = false;
  int bep = line.Find( cb ) + 1;
  int scs = line.Find( _T( '\'' ) ) + 1;
  int sce = line.Find( _T( '\'' ), true ) + 1;
  int dcs = line.Find( _T( '"' ) ) + 1;
  int dce = line.Find( _T( '"' ), true ) + 1;
  if( !scs && !sce && !dcs && !dce ) {
    bResult = true;
  }
  if( !( sce - scs ) && !( dce - dcs ) ) {
    bResult = true;
  }
  if( ( sce - scs ) && ( ( bep < scs ) || ( bep > sce ) ) ) {
    bResult = true;
  }
  if( ( dce - dcs ) && ( ( bep < dcs ) || ( bep > dce ) ) ) {
    bResult = true;
  }
  return bResult;
}

void DebuggerGDB::StepOut() {
  RunCommand( CMD_STEPOUT );
}

bool DebuggerGDB::RunToCursor( const wxString& filename, int line, const wxString& line_text ) {
  if( m_pProcess ) {
    m_State.AddBreakpoint( filename, line, true, line_text );
    Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
    Continue();
    return true;
  } else {
    if( !GetActiveConfigEx().GetFlag( DebuggerConfiguration::DoNotRun ) ) {
      m_State.AddBreakpoint( filename, line, true, line_text );
      Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
    }
    return Debug( false );
  }
}

void DebuggerGDB::SetNextStatement( const wxString& filename, int line ) {
  if( m_State.HasDriver() && IsStopped() ) {
    m_State.GetDriver()->SetNextStatement( filename, line );
  }
}

void DebuggerGDB::Break() {
  DoBreak( false );
}

void DebuggerGDB::DoBreak( bool temporary ) {
  m_TemporaryBreak = temporary;
  if( m_pProcess && m_Pid && !IsStopped() ) {
    long childPid = m_State.GetDriver()->GetChildPID();
    long pid = childPid;
    if( pid > 0 && !wxProcess::Exists( pid ) ) {
      DebugLog( F( _T( "子进程 (pid:%ld) 不存在" ), pid ), Logger::warning );
      pid = 0;
    }
    if( pid <= 0 ) {
      pid = m_Pid;
    }
    if( pid <= 0 ) {
      msging( _T( "无法停止调试进程!" ), _T( "错误" ), wxOK | wxICON_WARNING );
    } else {
      if( !wxProcess::Exists( pid ) ) {
        DebugLog( F( _T( "GDB进程 (pid:%ld) 不存在" ), pid ), Logger::error );
      }
      Log( F( _T( "试图用PID中断进程: %ld; 子PID: %ld GDB pid: %ld" ),
              pid, childPid, static_cast<long>( m_Pid ) ) );
      wxKillError error;
      if( wxKill( pid, wxSIGINT, &error ) != 0 ) {
        DebugLog( F( _T( "无法终止进程 (%ld) %d" ), pid, ( int )( error ) ) );
      }
    }
    PluginManager *plm = Manager::Get()->GetPluginManager();
    CodeBlocksEvent evt( cbEVT_DEBUGGER_PAUSED );
    plm->NotifyPlugins( evt );
  }
}

void DebuggerGDB::Stop() {
  if( m_pProcess && m_Pid ) {
    if( !IsStopped() ) {
      int childPID = m_State.GetDriver()->GetChildPID();
      if( childPID == 0 ) {
        DebugLog( _T( "子PID为0 因此直接终止GDB" ) );
        wxKill( m_Pid, wxSIGTERM );
        return;
      }
      Break();
    }
    RunCommand( CMD_STOP );
  }
}

void DebuggerGDB::ParseOutput( const wxString& output ) {
  if( !output.IsEmpty() && m_State.HasDriver() ) {
    m_State.GetDriver()->ParseOutput( output );
  }
}

void DebuggerGDB::GetCurrentPosition( wxString &filename, int &line ) {
  if( m_State.HasDriver() ) {
    const Cursor& cursor = m_State.GetDriver()->GetCursor();
    filename = cursor.file;
    line = cursor.line;
  } else {
    filename = wxEmptyString;
    line = -1;
  }
}

void DebuggerGDB::OnAddSymbolFile( wxCommandEvent& ( event ) ) {
  wxString file = wxFileSelector( _T( "选择要从中读取符号的文件" ),
                                  _T( "" ),
                                  _T( "" ),
                                  _T( "" ),
                                  _T( "可执行文件和库|*.exe;*.dll" ),
                                  wxFD_OPEN | wxFD_FILE_MUST_EXIST | compatibility::wxHideReadonly );
  if( file.IsEmpty() ) {
    return;
  }
  ConvertToGDBDirectory( file );
}

void DebuggerGDB::SetupToolsMenu( wxMenu &menu ) {
  if( !GetActiveConfigEx().IsGDB() ) {
    return;
  }
  menu.Append( idMenuInfoFrame, _T( "当前堆栈帧" ) );
  menu.Append( idMenuInfoDLL, _T( "加载的库" ) );
  menu.Append( idMenuInfoFiles, _T( "目标和文件" ) );
  menu.Append( idMenuInfoFPU, _T( "FPU状态" ) );
  menu.Append( idMenuInfoSignals, _T( "信号处理" ) );
  wxMenu *menuPrint = new wxMenu;
  menuPrint->AppendRadioItem( idMenuInfoPrintElementsUnlimited, _T( "Unlimited" ) );
  menuPrint->AppendRadioItem( idMenuInfoPrintElements20, _T( "20" ) );
  menuPrint->AppendRadioItem( idMenuInfoPrintElements50, _T( "50" ) );
  menuPrint->AppendRadioItem( idMenuInfoPrintElements100, _T( "100" ) );
  menuPrint->AppendRadioItem( idMenuInfoPrintElements200, _T( "200 (default)" ) );
  menu.AppendSubMenu( menuPrint, _T( "打印元素" ) );
  menu.AppendCheckItem( idMenuInfoCatchThrow, _T( "接球投掷" ) );
}

void DebuggerGDB::OnUpdateTools( wxUpdateUIEvent &event ) {
  bool checked = ( event.GetId() == idMenuInfoPrintElementsUnlimited && m_printElements == 0 ) ||
                 ( event.GetId() == idMenuInfoPrintElements20 && m_printElements == 20 ) ||
                 ( event.GetId() == idMenuInfoPrintElements50 && m_printElements == 50 ) ||
                 ( event.GetId() == idMenuInfoPrintElements100 && m_printElements == 100 ) ||
                 ( event.GetId() == idMenuInfoPrintElements200 && m_printElements == 200 );
  event.Check( checked );
  event.Enable( IsRunning() && IsStopped() );
}

void DebuggerGDB::OnPrintElements( wxCommandEvent &event ) {
  if( event.GetId() == idMenuInfoPrintElementsUnlimited ) {
    m_printElements = 0;
  } else if( event.GetId() == idMenuInfoPrintElements20 ) {
    m_printElements = 20;
  } else if( event.GetId() == idMenuInfoPrintElements50 ) {
    m_printElements = 50;
  } else if( event.GetId() == idMenuInfoPrintElements100 ) {
    m_printElements = 100;
  } else if( event.GetId() == idMenuInfoPrintElements200 ) {
    m_printElements = 200;
  } else {
    return;
  }
  wxString cmd = F( _T( "设置打印元素 %d" ), m_printElements );
  m_State.GetDriver()->QueueCommand( new DebuggerCmd( m_State.GetDriver(), cmd ) );
  RequestUpdate( Watches );
}

void DebuggerGDB::OnUpdateCatchThrow( wxUpdateUIEvent &event ) {
  DebuggerConfiguration &config = GetActiveConfigEx();
  event.Enable( config.IsGDB() && IsStopped() );
  event.Check( config.GetFlag( DebuggerConfiguration::CatchExceptions ) );
}

void DebuggerGDB::OnCatchThrow( wxCommandEvent &event ) {
  bool flag = event.IsChecked();
  GetActiveConfigEx().SetFlag( DebuggerConfiguration::CatchExceptions, flag );
  m_State.GetDriver()->EnableCatchingThrow( flag );
}

void DebuggerGDB::OnInfoFrame( wxCommandEvent& ( event ) ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->InfoFrame();
  }
}

void DebuggerGDB::OnInfoDLL( wxCommandEvent& ( event ) ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->InfoDLL();
  }
}

void DebuggerGDB::OnInfoFiles( wxCommandEvent& ( event ) ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->InfoFiles();
  }
}

void DebuggerGDB::OnInfoFPU( wxCommandEvent& ( event ) ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->InfoFPU();
  }
}

void DebuggerGDB::OnInfoSignals( wxCommandEvent& ( event ) ) {
  if( m_State.HasDriver() ) {
    m_State.GetDriver()->InfoSignals();
  }
}

void DebuggerGDB::OnGDBOutput( wxCommandEvent& event ) {
  wxString msg = event.GetString();
  if( !msg.IsEmpty() ) {
    ParseOutput( msg );
  }
}

void DebuggerGDB::OnGDBError( wxCommandEvent& event ) {
  wxString msg = event.GetString();
  if( !msg.IsEmpty() ) {
    ParseOutput( msg );
  }
}

void DebuggerGDB::OnGDBTerminated( wxCommandEvent& event ) {
  m_PidToAttach = 0;
  m_TimerPollDebugger.Stop();
  m_LastExitCode = event.GetInt();
  ClearActiveMarkFromAllEditors();
  m_State.StopDriver();
  Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
  if( !Manager::IsAppShuttingDown() ) {
    Log( F( _T( "调试程序已完成 状态为 %d" ), m_LastExitCode ) );
    if( m_NoDebugInfo ) {
      msging( _T( "此项目/目标没有调试信息." ), _T( "错误" ), wxICON_STOP );
    }
  }
  PluginManager *plm = Manager::Get()->GetPluginManager();
  CodeBlocksEvent evt( cbEVT_DEBUGGER_FINISHED );
  plm->NotifyPlugins( evt );
  if( !Manager::IsAppShuttingDown() ) {
    SwitchToPreviousLayout();
  }
  KillConsole();
  MarkAsStopped();
}

void DebuggerGDB::KillConsole() {
  if( m_bIsConsole && ( m_nConsolePid > 0 ) ) {
    ::wxKill( m_nConsolePid );
    m_nConsolePid = 0;
    m_bIsConsole = false;
  }
}

void DebuggerGDB::CheckIfConsoleIsClosed() {
  if( m_stopDebuggerConsoleClosed && m_nConsolePid > 0 && wxKill( m_nConsolePid, wxSIGNONE ) != 0 ) {
    if( msging( _T( "检测到终端/控制台已关闭是否停止调试会话?" ), _T( "调试器疑惑???" ), wxYES_NO ) == wxID_YES ) {
      m_stopDebuggerConsoleClosed = false;
    } else {
      Stop();
      m_nConsolePid = 0;
    }
  }
}

bool DebuggerGDB::ShowValueTooltip( int style ) {
  if( !m_pProcess || !IsStopped() ) {
    return false;
  }
  if( !m_State.HasDriver() || !m_State.GetDriver()->IsDebuggingStarted() ) {
    return false;
  }
  if( !GetActiveConfigEx().GetFlag( DebuggerConfiguration::EvalExpression ) ) {
    return false;
  }
  if( style != wxSCI_C_DEFAULT && style != wxSCI_C_OPERATOR && style != wxSCI_C_IDENTIFIER &&
      style != wxSCI_C_WORD2 && style != wxSCI_C_GLOBALCLASS ) {
    return false;
  }
  return true;
}

void DebuggerGDB::OnValueTooltip( const wxString &token, const wxRect &evalRect ) {
  m_State.GetDriver()->EvaluateSymbol( token, evalRect );
}

void DebuggerGDB::CleanupWhenProjectClosed( M_Code_Project *project ) {
  GetSearchDirs( project ).clear();
  GetRemoteDebuggingMap( project ).clear();
  DeleteAllProjectBreakpoints( project );
  cbBreakpointsDlg *dlg = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
  dlg->Reload();
}

void DebuggerGDB::OnIdle( wxIdleEvent& event ) {
  if( m_pProcess && ( ( PipedProcess* )m_pProcess )->HasInput() ) {
    event.RequestMore();
  } else {
    event.Skip();
  }
}

void DebuggerGDB::OnTimer( M_Code_unused wxTimerEvent& event ) {
  ParseOutput( wxEmptyString );
  CheckIfConsoleIsClosed();
  wxWakeUpIdle();
}

void DebuggerGDB::OnShowFile( wxCommandEvent& event ) {
  SyncEditor( event.GetString(), event.GetInt(), false );
}

void DebuggerGDB::DebuggeeContinued() {
  m_TemporaryBreak = false;
}

void DebuggerGDB::OnCursorChanged( wxCommandEvent& ( event ) ) {
  if( m_TemporaryBreak ) {
    return;
  }
  if( m_State.HasDriver() ) {
    const Cursor& cursor = m_State.GetDriver()->GetCursor();
    if( cursor.changed ) {
      bool autoSwitch = M_Code_DebuggerCommonConfig::GetFlag( M_Code_DebuggerCommonConfig::AutoSwitchFrame );
      MarkAllWatchesAsUnchanged();
      if( !autoSwitch || cursor.line != -1 ) {
        SyncEditor( cursor.file, cursor.line );
      }
      BringCBToFront();
      if( cursor.line != -1 ) {
        Log( F( _T( "At %s:%ld" ), cursor.file.wx_str(), cursor.line ) );
      } else
      { Log( F( _T( "In %s (%s)" ), cursor.function.wx_str(), cursor.file.wx_str() ) ); }
      DebuggerManager *dbg_manager = Manager::Get()->GetDebuggerManager();
      if( IsWindowReallyShown( dbg_manager->GetWatchesDialog()->GetWindow() ) ) {
        DoWatches();
      }
      if( dbg_manager->UpdateCPURegisters() ) {
        RunCommand( CMD_REGISTERS );
      }
      if( dbg_manager->UpdateBacktrace() ) {
        RunCommand( CMD_BACKTRACE );
      } else {
        if( cursor.line == -1 && autoSwitch ) {
          RunCommand( CMD_BACKTRACE );
        }
      }
      if( dbg_manager->UpdateDisassembly() ) {
        uint64_t addr = cbDebuggerStringToAddress( cursor.address );
        if( addr && !dbg_manager->GetDisassemblyDialog()->SetActiveAddress( addr ) ) {
          RunCommand( CMD_DISASSEMBLE );
        }
      }
      if( dbg_manager->UpdateExamineMemory() ) {
        RunCommand( CMD_MEMORYDUMP );
      }
      if( dbg_manager->UpdateThreads() ) {
        RunCommand( CMD_RUNNINGTHREADS );
      }
    }
  }
}

cb::shared_ptr<cbWatch> DebuggerGDB::AddWatch( const wxString& symbol ) {
  cb::shared_ptr<GDBWatch> watch( new GDBWatch( CleanStringValue( symbol ) ) );
  m_watches.push_back( watch );
  if( m_pProcess ) {
    m_State.GetDriver()->UpdateWatch( m_watches.back() );
  }
  return watch;
}

void DebuggerGDB::AddWatchNoUpdate( const cb::shared_ptr<GDBWatch> &watch ) {
  m_watches.push_back( watch );
}

void DebuggerGDB::DeleteWatch( cb::shared_ptr<cbWatch> watch ) {
  WatchesContainer::iterator it = std::find( m_watches.begin(), m_watches.end(), watch );
  if( it != m_watches.end() ) {
    m_watches.erase( it );
  }
}

bool DebuggerGDB::HasWatch( cb::shared_ptr<cbWatch> watch ) {
  WatchesContainer::iterator it = std::find( m_watches.begin(), m_watches.end(), watch );
  if( it != m_watches.end() ) {
    return true;
  } else {
    return watch == m_localsWatch || watch == m_funcArgsWatch;
  }
}

void DebuggerGDB::ShowWatchProperties( cb::shared_ptr<cbWatch> watch ) {
  if( watch->GetParent() ) {
    return;
  }
  cb::shared_ptr<GDBWatch> real_watch = cb::static_pointer_cast<GDBWatch>( watch );
  EditWatchDlg dlg( real_watch, nullptr );
  if( dlg.ShowModal() == wxID_OK ) {
    DoWatches();
  }
}

bool DebuggerGDB::SetWatchValue( cb::shared_ptr<cbWatch> watch, const wxString &value ) {
  if( !HasWatch( cbGetRootWatch( watch ) ) ) {
    return false;
  }
  if( !m_State.HasDriver() ) {
    return false;
  }
  wxString full_symbol;
  cb::shared_ptr<cbWatch> temp_watch = watch;
  while( temp_watch ) {
    wxString symbol;
    temp_watch->GetSymbol( symbol );
    temp_watch = temp_watch->GetParent();
    if( symbol.find( _T( '*' ) ) != wxString::npos || symbol.find( _T( '&' ) ) != wxString::npos ) {
      symbol = _T( '(' ) + symbol + _T( ')' );
    }
    if( full_symbol.empty() ) {
      full_symbol = symbol;
    } else
    { full_symbol = symbol + _T( '.' ) + full_symbol; }
  }
  DebuggerDriver* driver = m_State.GetDriver();
  driver->SetVarValue( full_symbol, value );
  DoWatches();
  return true;
}

void DebuggerGDB::ExpandWatch( M_Code_unused cb::shared_ptr<cbWatch> watch ) {
}

void DebuggerGDB::CollapseWatch( M_Code_unused cb::shared_ptr<cbWatch> watch ) {
}

void DebuggerGDB::UpdateWatch( cb::shared_ptr<cbWatch> watch ) {
  if( !HasWatch( watch ) ) {
    return;
  }
  if( !m_State.HasDriver() ) {
    return;
  }
  cb::shared_ptr<GDBWatch> real_watch = cb::static_pointer_cast<GDBWatch>( watch );
  if( real_watch == m_localsWatch ) {
    m_State.GetDriver()->UpdateWatchLocalsArgs( real_watch, true );
  } else if( real_watch == m_funcArgsWatch ) {
    m_State.GetDriver()->UpdateWatchLocalsArgs( real_watch, false );
  } else {
    m_State.GetDriver()->UpdateWatch( real_watch );
  }
}

void DebuggerGDB::MarkAllWatchesAsUnchanged() {
  if( m_localsWatch ) {
    m_localsWatch->MarkAsChangedRecursive( false );
  }
  if( m_funcArgsWatch ) {
    m_funcArgsWatch->MarkAsChangedRecursive( false );
  }
  for( WatchesContainer::iterator it = m_watches.begin(); it != m_watches.end(); ++it ) {
    ( *it )->MarkAsChangedRecursive( false );
  }
}

void DebuggerGDB::OnWatchesContextMenu( wxMenu &menu, const cbWatch &watch, wxObject *property, int &disabledMenus ) {
  wxString type, symbol;
  watch.GetType( type );
  watch.GetSymbol( symbol );
  if( IsPointerType( type ) ) {
    menu.Insert( 0, idMenuWatchDereference, _T( "Dereference " ) + symbol );
    m_watchToDereferenceSymbol = symbol;
    m_watchToDereferenceProperty = property;
  }
  if( watch.GetParent() ) {
    disabledMenus = WatchesDisabledMenuItems::Rename;
    disabledMenus |= WatchesDisabledMenuItems::Properties;
    disabledMenus |= WatchesDisabledMenuItems::Delete;
    disabledMenus |= WatchesDisabledMenuItems::AddDataBreak;
    disabledMenus |= WatchesDisabledMenuItems::ExamineMemory;
  }
}

void DebuggerGDB::OnMenuWatchDereference( M_Code_unused wxCommandEvent& event ) {
  cbWatchesDlg *watches = Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
  if( !watches ) {
    return;
  }
  watches->RenameWatch( m_watchToDereferenceProperty, _T( "*" ) + m_watchToDereferenceSymbol );
  m_watchToDereferenceProperty = NULL;
  m_watchToDereferenceSymbol = wxEmptyString;
}

void DebuggerGDB::AttachToProcess( const wxString& pid ) {
  if( !pid.IsEmpty() ) {
    pid.ToLong( ( long* )&m_PidToAttach );
    Debug( false );
  }
}

void DebuggerGDB::DetachFromProcess() {
  m_State.GetDriver()->Detach();
  m_PidToAttach = 0;
  m_State.GetDriver()->Stop();
}

bool DebuggerGDB::IsAttachedToProcess() const {
  return m_PidToAttach != 0;
}

bool DebuggerGDB::CompilerFinished( bool compilerFailed, StartType startType ) {
  if( compilerFailed || startType == StartTypeUnknown ) {
    return false;
  }
  if( DoDebug( startType == StartTypeStepInto ) != 0 ) {
    return false;
  }
  return true;
}

void DebuggerGDB::OnBuildTargetSelected( CodeBlocksEvent& event ) {
  if( m_pProject && event.GetProject() == m_pProject ) {
    m_ActiveBuildTarget = event.GetBuildTargetName();
  }
}
